<?php

require_once 'vendor/autoload.php';

use Smalot\Cups\Builder\Builder;
use Smalot\Cups\Manager\JobManager;
use Smalot\Cups\Manager\PrinterManager;
use Smalot\Cups\Model\Job;
use Smalot\Cups\Transport\ResponseParser;
use Http\Client\Socket\Client as HttpClient;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Uri;

/**
 * IPP网络连接修复器
 * 专门解决IPP网络打印失败的问题
 */
class IPPNetworkFix
{
    private string $printerIp;
    private int $printerPort;
    private Builder $builder;
    private ResponseParser $responseParser;

    public function __construct(string $printerIp = '10.1.0.21', int $printerPort = 631)
    {
        $this->printerIp = $printerIp;
        $this->printerPort = $printerPort;
        $this->builder = new Builder();
        $this->responseParser = new ResponseParser();
        
        echo "🔧 IPP网络连接修复器\n";
        echo "目标打印机: {$this->printerIp}:{$this->printerPort}\n";
        echo str_repeat('=', 60) . "\n\n";
    }

    /**
     * 运行网络修复
     */
    public function runNetworkFix(): bool
    {
        echo "🚀 开始IPP网络连接修复...\n\n";
        
        // 方法1: 使用本地回环修复
        if ($this->tryLoopbackFix()) {
            echo "✅ 本地回环修复成功！\n";
            return true;
        }
        
        // 方法2: 使用直接Socket连接
        if ($this->tryDirectSocketFix()) {
            echo "✅ 直接Socket连接成功！\n";
            return true;
        }
        
        // 方法3: 使用HTTP代理修复
        if ($this->tryHTTPProxyFix()) {
            echo "✅ HTTP代理修复成功！\n";
            return true;
        }
        
        // 方法4: 使用原始IPP协议
        if ($this->tryRawIPPFix()) {
            echo "✅ 原始IPP协议成功！\n";
            return true;
        }
        
        echo "❌ 所有网络修复方法都失败了\n";
        return false;
    }

    /**
     * 方法1: 本地回环修复
     * 通过本地代理解决网络连接问题
     */
    private function tryLoopbackFix(): bool
    {
        echo "🔄 尝试本地回环修复...\n";
        
        try {
            // 创建一个自定义的HTTP客户端，强制使用localhost
            $customClient = new class($this->printerIp, $this->printerPort) implements \Http\Client\HttpClient {
                private string $targetIp;
                private int $targetPort;
                
                public function __construct(string $ip, int $port)
                {
                    $this->targetIp = $ip;
                    $this->targetPort = $port;
                }
                
                public function sendRequest(RequestInterface $request): ResponseInterface
                {
                    // 创建一个临时的socket连接
                    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
                    if (!$socket) {
                        throw new \Exception('无法创建socket');
                    }
                    
                    socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 30, 'usec' => 0]);
                    socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, ['sec' => 30, 'usec' => 0]);
                    
                    $result = socket_connect($socket, $this->targetIp, $this->targetPort);
                    if (!$result) {
                        socket_close($socket);
                        throw new \Exception('无法连接到打印机');
                    }
                    
                    // 构建HTTP请求
                    $path = $request->getUri()->getPath() ?: '/ipp/print';
                    $httpRequest = $request->getMethod() . " {$path} HTTP/1.1\r\n";
                    $httpRequest .= "Host: {$this->targetIp}:{$this->targetPort}\r\n";
                    $httpRequest .= "Content-Type: application/ipp\r\n";
                    $httpRequest .= "Content-Length: " . strlen($request->getBody()) . "\r\n";
                    $httpRequest .= "User-Agent: CUPS-IPP-Fix/1.0\r\n";
                    $httpRequest .= "Connection: close\r\n";
                    $httpRequest .= "\r\n";
                    $httpRequest .= $request->getBody();
                    
                    // 发送请求
                    socket_write($socket, $httpRequest, strlen($httpRequest));
                    
                    // 读取响应
                    $response = '';
                    while ($buffer = socket_read($socket, 4096)) {
                        $response .= $buffer;
                    }
                    
                    socket_close($socket);
                    
                    // 解析HTTP响应
                    $parts = explode("\r\n\r\n", $response, 2);
                    $headers = $parts[0] ?? '';
                    $body = $parts[1] ?? '';
                    
                    // 提取状态码
                    preg_match('/HTTP\/\d\.\d (\d+)/', $headers, $matches);
                    $statusCode = isset($matches[1]) ? (int)$matches[1] : 200;
                    
                    return new \GuzzleHttp\Psr7\Response($statusCode, [], $body);
                }
            };
            
            $jobManager = new JobManager($this->builder, $customClient, $this->responseParser);
            $printerManager = new PrinterManager($this->builder, $customClient, $this->responseParser);
            
            return $this->sendTestJob($jobManager, $printerManager);
            
        } catch (Exception $e) {
            echo "   错误: {$e->getMessage()}\n";
            return false;
        }
    }

    /**
     * 方法2: 直接Socket连接
     */
    private function tryDirectSocketFix(): bool
    {
        echo "🔌 尝试直接Socket连接...\n";
        
        try {
            // 使用fsockopen进行直接连接
            $customClient = new class($this->printerIp, $this->printerPort) implements \Http\Client\HttpClient {
                private string $targetIp;
                private int $targetPort;
                
                public function __construct(string $ip, int $port)
                {
                    $this->targetIp = $ip;
                    $this->targetPort = $port;
                }
                
                public function sendRequest(RequestInterface $request): ResponseInterface
                {
                    $fp = fsockopen($this->targetIp, $this->targetPort, $errno, $errstr, 30);
                    if (!$fp) {
                        throw new \Exception("连接失败: {$errstr} ({$errno})");
                    }
                    
                    stream_set_timeout($fp, 30);
                    
                    // 构建HTTP请求
                    $path = $request->getUri()->getPath() ?: '/ipp/print';
                    $httpRequest = $request->getMethod() . " {$path} HTTP/1.1\r\n";
                    $httpRequest .= "Host: {$this->targetIp}:{$this->targetPort}\r\n";
                    $httpRequest .= "Content-Type: application/ipp\r\n";
                    $httpRequest .= "Content-Length: " . strlen($request->getBody()) . "\r\n";
                    $httpRequest .= "User-Agent: CUPS-IPP-Direct/1.0\r\n";
                    $httpRequest .= "Accept: application/ipp\r\n";
                    $httpRequest .= "Accept-Language: en-us\r\n";
                    $httpRequest .= "Accept-Encoding: identity\r\n";
                    $httpRequest .= "Connection: close\r\n";
                    $httpRequest .= "\r\n";
                    $httpRequest .= $request->getBody();
                    
                    // 发送请求
                    fwrite($fp, $httpRequest);
                    
                    // 读取响应
                    $response = '';
                    while (!feof($fp)) {
                        $response .= fread($fp, 8192);
                    }
                    
                    fclose($fp);
                    
                    // 解析HTTP响应
                    $parts = explode("\r\n\r\n", $response, 2);
                    $headers = $parts[0] ?? '';
                    $body = $parts[1] ?? '';
                    
                    // 提取状态码
                    preg_match('/HTTP\/\d\.\d (\d+)/', $headers, $matches);
                    $statusCode = isset($matches[1]) ? (int)$matches[1] : 200;
                    
                    return new \GuzzleHttp\Psr7\Response($statusCode, [], $body);
                }
            };
            
            $jobManager = new JobManager($this->builder, $customClient, $this->responseParser);
            $printerManager = new PrinterManager($this->builder, $customClient, $this->responseParser);
            
            return $this->sendTestJob($jobManager, $printerManager);
            
        } catch (Exception $e) {
            echo "   错误: {$e->getMessage()}\n";
            return false;
        }
    }

    /**
     * 方法3: HTTP代理修复
     */
    private function tryHTTPProxyFix(): bool
    {
        echo "🌐 尝试HTTP代理修复...\n";
        
        try {
            // 使用cURL进行HTTP请求
            $customClient = new class($this->printerIp, $this->printerPort) implements \Http\Client\HttpClient {
                private string $targetIp;
                private int $targetPort;
                
                public function __construct(string $ip, int $port)
                {
                    $this->targetIp = $ip;
                    $this->targetPort = $port;
                }
                
                public function sendRequest(RequestInterface $request): ResponseInterface
                {
                    $ch = curl_init();
                    
                    $url = "http://{$this->targetIp}:{$this->targetPort}" . ($request->getUri()->getPath() ?: '/ipp/print');
                    
                    curl_setopt_array($ch, [
                        CURLOPT_URL => $url,
                        CURLOPT_POST => true,
                        CURLOPT_POSTFIELDS => (string)$request->getBody(),
                        CURLOPT_RETURNTRANSFER => true,
                        CURLOPT_TIMEOUT => 30,
                        CURLOPT_CONNECTTIMEOUT => 10,
                        CURLOPT_HTTPHEADER => [
                            'Content-Type: application/ipp',
                            'Host: ' . $this->targetIp . ':' . $this->targetPort,
                            'User-Agent: CUPS-IPP-Proxy/1.0',
                            'Accept: application/ipp',
                            'Accept-Language: en-us',
                            'Accept-Encoding: identity',
                            'Connection: close'
                        ],
                        CURLOPT_HEADER => true,
                        CURLOPT_NOBODY => false,
                        CURLOPT_FOLLOWLOCATION => false,
                        CURLOPT_SSL_VERIFYPEER => false,
                        CURLOPT_SSL_VERIFYHOST => false
                    ]);
                    
                    $response = curl_exec($ch);
                    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                    $error = curl_error($ch);
                    curl_close($ch);
                    
                    if ($response === false) {
                        throw new \Exception("cURL错误: {$error}");
                    }
                    
                    // 分离头部和主体
                    $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
                    $body = substr($response, $headerSize);
                    
                    return new \GuzzleHttp\Psr7\Response($httpCode, [], $body);
                }
            };
            
            $jobManager = new JobManager($this->builder, $customClient, $this->responseParser);
            $printerManager = new PrinterManager($this->builder, $customClient, $this->responseParser);
            
            return $this->sendTestJob($jobManager, $printerManager);
            
        } catch (Exception $e) {
            echo "   错误: {$e->getMessage()}\n";
            return false;
        }
    }

    /**
     * 方法4: 原始IPP协议
     */
    private function tryRawIPPFix(): bool
    {
        echo "📡 尝试原始IPP协议...\n";
        
        try {
            // 直接发送IPP请求
            $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
            if (!$socket) {
                throw new \Exception('无法创建socket');
            }
            
            socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 30, 'usec' => 0]);
            socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, ['sec' => 30, 'usec' => 0]);
            
            $result = socket_connect($socket, $this->printerIp, $this->printerPort);
            if (!$result) {
                socket_close($socket);
                throw new \Exception('无法连接到打印机');
            }
            
            // 构建IPP Get-Printer-Attributes请求
            $ippRequest = $this->buildIPPGetPrinterAttributesRequest();
            
            // 构建HTTP头
            $httpRequest = "POST /ipp/print HTTP/1.1\r\n";
            $httpRequest .= "Host: {$this->printerIp}:{$this->printerPort}\r\n";
            $httpRequest .= "Content-Type: application/ipp\r\n";
            $httpRequest .= "Content-Length: " . strlen($ippRequest) . "\r\n";
            $httpRequest .= "User-Agent: CUPS-IPP-Raw/1.0\r\n";
            $httpRequest .= "Accept: application/ipp\r\n";
            $httpRequest .= "Accept-Language: en-us\r\n";
            $httpRequest .= "Accept-Encoding: identity\r\n";
            $httpRequest .= "Connection: close\r\n";
            $httpRequest .= "\r\n";
            $httpRequest .= $ippRequest;
            
            // 发送请求
            socket_write($socket, $httpRequest, strlen($httpRequest));
            
            // 读取响应
            $response = '';
            while ($buffer = socket_read($socket, 4096)) {
                $response .= $buffer;
            }
            
            socket_close($socket);
            
            // 检查响应
            if (strpos($response, 'HTTP/1.1 200') !== false || strpos($response, 'HTTP/1.0 200') !== false) {
                echo "   ✅ 原始IPP协议连接成功\n";
                return true;
            } else {
                echo "   ❌ 原始IPP协议连接失败\n";
                return false;
            }
            
        } catch (Exception $e) {
            echo "   错误: {$e->getMessage()}\n";
            return false;
        }
    }

    /**
     * 构建IPP Get-Printer-Attributes请求
     */
    private function buildIPPGetPrinterAttributesRequest(): string
    {
        // IPP版本 (2.0)
        $ipp = pack('CC', 2, 0);
        
        // 操作ID (Get-Printer-Attributes = 0x000B)
        $ipp .= pack('n', 0x000B);
        
        // 请求ID
        $ipp .= pack('N', 1);
        
        // 操作属性组标签
        $ipp .= pack('C', 0x01);
        
        // attributes-charset
        $ipp .= pack('C', 0x47); // charset tag
        $ipp .= pack('n', 18); // name length
        $ipp .= 'attributes-charset';
        $ipp .= pack('n', 5); // value length
        $ipp .= 'utf-8';
        
        // attributes-natural-language
        $ipp .= pack('C', 0x48); // naturalLanguage tag
        $ipp .= pack('n', 27); // name length
        $ipp .= 'attributes-natural-language';
        $ipp .= pack('n', 5); // value length
        $ipp .= 'en-us';
        
        // printer-uri
        $printerUri = "ipp://{$this->printerIp}:{$this->printerPort}/ipp/print";
        $ipp .= pack('C', 0x45); // uri tag
        $ipp .= pack('n', 11); // name length
        $ipp .= 'printer-uri';
        $ipp .= pack('n', strlen($printerUri)); // value length
        $ipp .= $printerUri;
        
        // 结束标签
        $ipp .= pack('C', 0x03);
        
        return $ipp;
    }

    /**
     * 发送测试打印任务
     */
    private function sendTestJob(JobManager $jobManager, PrinterManager $printerManager): bool
    {
        try {
            $printerUri = "ipp://{$this->printerIp}:{$this->printerPort}/ipp/print";
            
            $printer = $printerManager->findByUri($printerUri);
            if (!$printer) {
                echo "   ❌ 无法找到打印机\n";
                return false;
            }
            
            $job = new Job();
            $job->setName('IPP网络修复测试');
            $job->setUsername('network_fix');
            $job->setCopies(1);
            $job->setPrinterUri($printerUri);
            $job->addText($this->createTestContent());
            
            $result = $jobManager->send($printer, $job);
            
            if ($result) {
                echo "   ✅ 测试打印任务发送成功\n";
                return true;
            } else {
                echo "   ❌ 测试打印任务发送失败\n";
                return false;
            }
            
        } catch (Exception $e) {
            echo "   ❌ 发送测试任务时出错: {$e->getMessage()}\n";
            return false;
        }
    }

    /**
     * 创建测试内容
     */
    private function createTestContent(): string
    {
        return "IPP网络连接修复测试\n" .
               "========================\n" .
               "时间: " . date('Y-m-d H:i:s') . "\n" .
               "打印机: {$this->printerIp}:{$this->printerPort}\n" .
               "测试类型: 网络修复\n" .
               "\n" .
               "如果您看到这个文档，说明IPP网络连接已修复！\n" .
               "\n" .
               "修复完成时间: " . date('Y-m-d H:i:s') . "\n";
    }
}

// 运行网络修复
try {
    // 可以修改IP地址为您的打印机IP
    $networkFix = new IPPNetworkFix('10.1.0.21', 631);
    $success = $networkFix->runNetworkFix();
    
    if ($success) {
        echo "\n🎉 IPP网络连接修复成功！\n";
        echo "现在可以正常使用IPP打印功能了。\n";
    } else {
        echo "\n😞 IPP网络连接修复失败。\n";
        echo "建议使用Windows系统打印作为备用方案。\n";
    }
    
    exit($success ? 0 : 1);
    
} catch (Exception $e) {
    echo "❌ 网络修复器运行失败: {$e->getMessage()}\n";
    echo "📍 位置: {$e->getFile()}:{$e->getLine()}\n";
    exit(1);
}